﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using StockUtil;

namespace StockLib.Data
{
    public class KFileSaver
    {
        public virtual void Save(Stock stock, KType kType, List<KData> kData)
        {
            const int PACK_NUM = 500;

            if (kData.Count <= 0)
                return;

            FileInfo fi = new FileInfo(StockData.Instance.GetFileName(stock, DataType.K, kType));
            FileStream fs = null;

            try
            {
                if (!fi.Directory.Exists)
                    Directory.CreateDirectory(fi.Directory.FullName);

                fs = new FileStream(fi.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);

                int dataFirstTime = kData[0].Time;
                int dataLastTime = kData[kData.Count - 1].Time;
                int fileFirstTime = -1;
                int fileLastTime = -1;
                int fileBeginIndex = -1;
                int fileEndIndex = -1;
                int fileCount = 0;

                int packSize = Marshal.SizeOf(typeof(KFilePack));
                byte[] buffer = new byte[packSize * PACK_NUM];
                int byteNumber = 0;
                while ((byteNumber = fs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    for (int i = 0; i < byteNumber / packSize; i++)
                    {
                        KFilePack pack = (KFilePack)StructHelper.FromBytes(buffer, i * packSize, typeof(KFilePack));

                        fileLastTime = pack.m_time;
                        if (fileFirstTime < 0)
                            fileFirstTime = pack.m_time;

                        if (fileBeginIndex < 0 && pack.m_time >= dataFirstTime)
                        {
                            fileBeginIndex = fileCount;
                        }
                        if (fileEndIndex < 0 && pack.m_time > dataLastTime)
                        {
                            fileEndIndex = fileCount;
                        }

                        fileCount++;
                    }
                }

                if (dataFirstTime >= fileFirstTime)
                {
                    if (dataFirstTime > fileLastTime)
                    {
                        fileBeginIndex = fileCount;
                    }
                    fs.Seek(fileBeginIndex * packSize, SeekOrigin.Begin);
                    int writeTimes = Convert.ToInt32(Math.Ceiling(1.0 * kData.Count / PACK_NUM));
                    for (int i = 0; i < writeTimes; i++)
                    {
                        int packNumber = (i == writeTimes - 1) ? (kData.Count - i * PACK_NUM) : PACK_NUM;
                        for (int j = 0; j < packNumber; j++)
                        {
                            KFilePack pack = KFilePack.FromInternal(kData[i * PACK_NUM + j]);
                            StructHelper.ToBytes(pack, buffer, j * packSize);
                        }
                        fs.Write(buffer, 0, packNumber * packSize);
                    }
                }
                else
                {
                    FileStream fs2 = new FileStream(fi.FullName + ".tmp", FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
                    try
                    {
                        int writeTimes = Convert.ToInt32(Math.Ceiling(1.0 * kData.Count / PACK_NUM));
                        for (int i = 0; i < writeTimes; i++)
                        {
                            int packNumber = (i == writeTimes - 1) ? (kData.Count - i * PACK_NUM) : PACK_NUM;
                            for (int j = 0; j < packNumber; j++)
                            {
                                KFilePack pack = KFilePack.FromInternal(kData[i * PACK_NUM + j]);
                                StructHelper.ToBytes(pack, buffer, j * packSize);
                            }
                            fs2.Write(buffer, 0, packNumber * packSize);
                        }
                        if (fileEndIndex > 0 && fileEndIndex < fileCount)
                        {
                            fs.Seek(fileEndIndex * packSize, SeekOrigin.Begin);
                            byteNumber = 0;
                            while ((byteNumber = fs.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                fs2.Write(buffer, 0, byteNumber);
                            }
                        }
                        fs.Close();
                        fs2.Close();

                        File.Copy(fs2.Name, fs.Name, true);
                        File.Delete(fs2.Name);

                        fs.Dispose();
                        fs = null;

                        fs2.Dispose();
                        fs2 = null;
                    }
                    catch (Exception ex2)
                    {
                        Trace.WriteLine(ex2.ToString());
                        throw ex2;
                    }
                    finally
                    {
                        if (fs2 != null)
                        {
                            fs2.Close();
                            fs2.Dispose();
                        }
                    }
                }

                /*foreach (KData data in kData)
                {
                    KFilePack pack = KFilePack.FromInternal(data);
                    byte[] buffer = StructHelper.ToBytes(pack);

                    bool hasPack = false;
                    bool insertPack = false;
                    byte[] bufferOld = new byte[Marshal.SizeOf(typeof(KFilePack))];
                    while (fs.Read(bufferOld, 0, bufferOld.Length) > 0)
                    {
                        KFilePack packOld = (KFilePack)StructHelper.FromBytes(bufferOld, typeof(KFilePack));

                        if (pack.m_time == packOld.m_time)
                        {
                            hasPack = true;
                            fs.Seek(fs.Position - bufferOld.Length, SeekOrigin.Begin);
                            break;
                        }

                        if (pack.m_time < packOld.m_time)
                        {
                            insertPack = true;
                            break;
                        }
                    }

                    if (hasPack || !insertPack)
                    {
                        fs.Write(buffer, 0, buffer.Length);
                    }
                    else if (insertPack)
                    {
                        int flen = (int)fs.Length;
                        int fpos = (int)fs.Position;
                        byte[] bufferTemp = new byte[flen - fpos + 1];
                        if (bufferTemp != null)
                        {
                            if (flen - fpos > 0)
                                fs.Read(bufferTemp, 0, flen - fpos);

                            fs.Seek(fpos - bufferOld.Length, SeekOrigin.Begin);
                            fs.Write(buffer, 0, buffer.Length);
                            fs.Write(bufferOld, 0, bufferOld.Length);
                            if (flen - fpos > 0)
                                fs.Write(bufferTemp, 0, flen - fpos);
                            bufferTemp = null;
                        }
                        fs.Seek(fpos, SeekOrigin.Begin);
                    }
                }*/
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.ToString());
                throw ex;
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                    fs.Dispose();
                }
            }
        }
    }
}
